from django.db import models
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.db.models import Q

class Project(models.Model):
    name = models.CharField(max_length=256)
    completionDate = models.DateField(blank=True, null=True)
    
    def __unicode__(self):
        return self.name
    

class Repository(models.Model):
    name = models.CharField(max_length=256)
    project = models.ForeignKey(Project)
    url = models.URLField()
    username = models.CharField(blank=True, null=True, max_length=256)
    password = models.CharField(blank=True, null=True, max_length=256)
        
    def __unicode__(self):
        return self.name

class Sprint(models.Model):
    name = models.CharField(max_length=256)
    project = models.ForeignKey(Project)
    startDate = models.DateField()
    endDate = models.DateField()
    users = models.ManyToManyField(User, related_name='+Sprint', blank=True)
    
    def clean(self):
        if self.startDate is None:
            raise ValidationError('Must have a start date!')
        if self.endDate is None:
            raise ValidationError('Must have an end date!')
        if self.endDate < self.startDate:
            raise ValidationError('End date must be after start date.')
        if self.project.completionDate != None: 
            if self.startDate > self.project.completionDate:
                raise ValidationError('Sprint cannot start after a projects completion date.')
            if self.endDate > self.project.completionDate:
                raise ValidationError('Sprint cannot end after a projects completion date.')
        conflicts = Sprint.objects.filter(#sprint's start is within this sprint.
                                          (Q(startDate__gte = self.startDate) & Q(startDate__lte = self.endDate)) |
                                          #sprint's end is within this sprint.
                                          (Q(endDate__gte = self.startDate) & Q(endDate__lte = self.endDate)) |
                                          #this sprint is entirely contained within a sprint.
                                          (Q(startDate__lte = self.startDate) & Q(endDate__gte = self.endDate)),
                                          project=self.project).exclude(pk=self.pk)
        if conflicts.count() > 0:
            message = "Sprint cannot overlap with other sprint's date ranges. The following sprints are in conflict: \n"
            for con in conflicts:
                message += "%s\n" % con.name
            raise ValidationError(message)
        
    def __unicode__(self):
        return self.name

class Scrum(models.Model):
    name = models.CharField(max_length=256)
    sprint = models.ForeignKey(Sprint)
    day = models.IntegerField()
    time = models.TimeField()

    def daycalc(self):
      daydict = {
        0: "Monday",
        1: "Tuesday",
        2: "Wednesday",
        3: "Thursday",
        4: "Friday",
        5: "Saturday",
        6: "Sunday"
      }
      return daydict[self.day]

    daytext = property(daycalc)

    def __unicode__(self):
        return self.name

class Priority(models.Model):
    name = models.CharField(max_length=128)
    sortOrder = models.IntegerField()
    
    def __unicode__(self):
        return self.name

class Status(models.Model):
    name = models.CharField(max_length=128)
    sortOrder = models.IntegerField()
    
    def __unicode__(self):
        return self.name
    
class TaskType(models.Model):
    name = models.CharField(max_length=128)
    sortOrder = models.IntegerField()
    
    def __unicode__(self):
        return self.name
    
class Task(models.Model):
    name = models.CharField(max_length=256)
    project = models.ForeignKey(Project)
    sprint = models.ForeignKey(Sprint, blank=True, null=True, on_delete=models.SET_NULL)
    type = models.ForeignKey(TaskType)
    parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.SET_NULL)
    priority = models.ForeignKey(Priority)
    status = models.ForeignKey(Status)
    description = models.CharField(max_length=1024)
    estimatedHours = models.FloatField()
    workedHours = models.FloatField()
    users = models.ManyToManyField(User, related_name='+', blank=True)
    revisions = models.ManyToManyField('Revision', related_name="rev+", blank=True)
    
    
    def clean(self):
        if self.sprint != None and self.sprint.project.pk != self.project.pk:
            raise ValidationError('Selected sprint must belong to the same project as this task.')
        if self.parent != None and self.parent == self:
            raise ValidationError('Task cannot be its own parent.')
        if self.parent != None and self.parent.parent != None:
            raise ValidationError('Parent task must not have a parent')
        if self.estimatedHours < 0:
            raise ValidationError('Estimated Hours cannot be negative.')
        if self.workedHours < 0:
            raise ValidationError('Worked Hours cannot be negative.')
    
    def __unicode__(self):
        return self.name
    
class Revision(models.Model):
    number = models.IntegerField()
    repo = models.ForeignKey(Repository)
    author = models.CharField(max_length=256)
    timestamp = models.DateTimeField()
    message = models.TextField()
    tasks = models.ManyToManyField('Task', through=Task.revisions.through, blank=True)
    
    def __unicode__(self):
        return '%s | r%s | %s' % (self.repo.name, self.number, self.message)
    
class Comment(models.Model):
    user = models.ForeignKey(User)
    tasks = models.ForeignKey(Task)
    time = models.DateTimeField()
    contents = models.TextField()
    
    def __unicode__(self):
        return '%s - %s - %s' % (self.user.username, self.tasks.name, self.time)
    
class TaskSearchCriteria(models.Model):
    name = models.CharField(max_length=256)
    task_name = models.CharField(max_length=256, blank=True, null=True)
    project = models.ForeignKey(Project, blank=True, null=True)
    sprint = models.ForeignKey(Sprint, blank=True, null=True)
    type = models.ForeignKey(TaskType, blank=True, null=True)
    priority = models.ForeignKey(Priority, blank=True, null=True)
    status = models.ForeignKey(Status, blank=True, null=True)
    description = models.CharField(max_length=1024, blank=True, null=True)
    owner = models.ForeignKey(User)
    users = models.ManyToManyField(User, related_name='a+', blank=True)
    
    def get_param_dict(self, uids = []):
        retval = dict()
        if self.task_name != None and len(self.task_name) > 0:
            retval["name__icontains"] = self.task_name
        if self.project != None:
            retval["project__id"] = self.project.pk
        if self.sprint != None:
            retval["sprint__id"] = self.sprint.pk
        if self.type != None:
            retval["type__id"] = self.type.pk
        if self.priority != None:
            retval["priority__id"] = self.priority.pk
        if self.status != None:
            retval["status__id"] = self.status.pk
        if self.description != None and len(self.description) > 0:
            retval["description__icontains"] = self.description
        if self.pk and self.users != None and self.users.count() > 0:
            ids = [user_id[0] for user_id in self.users.all().values_list('pk')]
            retval["users__in"] = ids
        elif not self.pk and uids != None and len(uids) > 0:
            retval["users__in"] = uids
        return retval
    
    def __unicode__(self):
        return self.name


        
